package edu.columbia.cloudbox.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.List;
import java.util.Vector;

import android.content.Context;
import android.util.Log;

public class LocalIO {

	private Context c;
	private static String className = LocalIO.class.getSimpleName();
	private String userName;
	private File localDir;

	public LocalIO(String userName, Context c) {
		final String methodName = "Constructor";
		Log.i(className, methodName + ": Entering...");
		this.userName = userName;
		localDir = c.getFilesDir();
		File userFolder = new File(localDir, userName);
		File cacheFolder = new File(localDir, userName + "_cache/" + userName);
		userFolder.mkdirs();
		cacheFolder.mkdirs();

		this.c = c;
	}

	public File getLocalDir() {
		return localDir;
	}

	public boolean doesFileExist(String filePath) {
		File file = new File(localDir, filePath);
		return file.exists();
	}

	/** get output stream */
	public OutputStream getOutputStream(String fileName) {
		final String methodName = "getOutputStream(" + fileName + ")";
		Log.i(className, methodName + ": Entering...");
		FileOutputStream fos = null;

		File localDir = c.getFilesDir();
		File fileDir = new File(localDir, userName);
		fileDir.mkdirs();
		File file = new File(fileDir, fileName);

		try {
			file.createNewFile();
			fos = new FileOutputStream(file);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		Log.i(className, methodName + ": Returning...");
		return fos;
	}

	/** get input stream */
	public FileInputStream getInputStream(String fileName) {
		final String methodName = "getInputStream(" + fileName + ")";
		Log.i(className, methodName + ": Entering...");
		FileInputStream fis = null;

		File localDir = c.getFilesDir();
		File fileDir = new File(localDir, userName);
		File file = new File(fileDir, fileName);

		if (!file.exists()) {
			return null;
		}

		try {
			fis = new FileInputStream(file);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return fis;
	}

	/**
	 * Delete a file on local storage
	 * 
	 * @param fileName
	 *            file path starting from
	 *            /data/data/edu.columbia.cloudbox/files/
	 * */
	public boolean delete(String fileName) {
		final String methodName = "Delete";
		Log.i(className, methodName + ": Entering...");
		File localDir = c.getFilesDir();
		Log.i(className, methodName + ": localDir: " + localDir.getPath());

		File targetFile = new File(localDir, fileName);
		Log.i(className, methodName + ": targetFile: " + targetFile.getPath());

		boolean deleted = targetFile.delete();
		Log.i(className, methodName + ": deleted: " + deleted);

		return deleted;

	}

	/**
	 * Delete a folder
	 * 
	 * @param folderName
	 *            file path starting from
	 *            /data/data/edu.columbia.cloudbox/files/
	 * */
	public boolean deleteFolder(String folderName) {
		final String methodName = "DeleteFolder(" + folderName + ")";
		Log.i(className, methodName + ": Entering...");

		File localDir = c.getFilesDir();
		Log.i(className, methodName + ": localDir: " + localDir.getPath());

		File targetFolder = new File(localDir, folderName);
		Log.i(className,
				methodName + ": targetFolder: " + targetFolder.getPath());

		File[] targetFiles = targetFolder.listFiles();
		for (File targetFile : targetFiles) {
			if (targetFile.isDirectory()) {
				Log.i(className, methodName + ": Ready to delete folder.");
				deleteFolder(folderName + "/" + targetFile.getName());
			} else {
				Log.i(className, methodName + ": Ready to delete file.");
				targetFile.delete();
			}
		}
		return targetFolder.delete();
	}

	/**
	 * Display a folder
	 * 
	 * @param folder
	 *            folder path starting from
	 *            /data/data/edu.columbia.cloudbox/files/
	 * */
	public List<FileType> getFileTypesByFolder(String folderName) {
		final String methodName = "getFileTypesByFolder(" + folderName + ")";
		Log.i(className, methodName + ": Entering...");

		List<FileType> resultList = new Vector<FileType>();

		File localDir = c.getFilesDir();
		Log.i(className, methodName + ": localDir: " + localDir.getPath());

		File targetFolder = new File(localDir, folderName);
		Log.i(className,
				methodName + ": targetFolder: " + targetFolder.getPath());

		File[] targetFiles = targetFolder.listFiles();
		for (File targetFile : targetFiles) {
			String fileName = targetFile.getName();
			if (targetFile.isDirectory()) {
				resultList.add(new FileType(fileName, true, "folder"));
			} else if (targetFile.isFile()) {
				int dotIdx = fileName.lastIndexOf(".");
				String ext = (dotIdx == -1) ? "" : fileName
						.substring(dotIdx + 1);
				resultList.add(new FileType(fileName, false, ext));
			}
		}

		Collections.sort(resultList);
		Log.i(className, methodName + ": Return: " + resultList);

		return resultList;
	}

	/**
	 * Get all files within a folder (recursively)
	 * 
	 * @param folderName
	 *            folder path starting from
	 *            /data/data/edu.columbia.cloudbox/files/
	 * */
	public List<String> getPathsByFolder(String folderName) {
		final String methodName = "getPathsByFolder(" + folderName + ")";
		Log.i(className, methodName + ": Entering...");
		List<String> resultList = new Vector<String>();

		File localDir = c.getFilesDir();
		File folderDir = new File(localDir, folderName);
		File[] files = folderDir.listFiles();

		for (File file : files) {

			String path = folderName + "/" + file.getName();
			if (file.isDirectory()) {
				Log.i(className, methodName + ": " + path + "is a folder");
				resultList.addAll(getPathsByFolder(folderName + "/"
						+ file.getName()));
			} else {
				Log.i(className, methodName + ": " + path + "is a file");
				resultList.add(path);
			}
		}

		Log.i(className, methodName + ": Return: " + resultList);
		return resultList;

	}

	/** get all files' file type included by a folder */
	public List<FileType> getAllFileTypesbyFolder(String folderName) {
		final String methodName = "getAllFileTypesbyFolder(" + folderName + ")";
		Log.i(className, methodName + ": Entering...");

		if (folderName == null || folderName.equals("")) {
			return null;
		}

		List<FileType> resultList = new Vector<FileType>();

		List<String> keys = this.getPathsByFolder(folderName);
		for (String key : keys) {
			int slashIdx = key.lastIndexOf("/");
			String fileName = key.substring(slashIdx + 1);

			int dotIdx = fileName.lastIndexOf(".");
			String ext = (dotIdx == -1) ? "" : fileName.substring(dotIdx + 1);
			resultList.add(new FileType(fileName, false, ext, key));
		}

		Log.i(className, methodName + ": Return: " + resultList);
		return resultList;
	}

	/** Move file from filepath1 to filepath2 */
	public boolean moveFileTo(String filePath1, String filePath2) {
		final String methodName = "moveFileTo(" + filePath1 + ", " + filePath2
				+ ")";
		Log.i(className, methodName + ": Entering...");

		File localDir = c.getFilesDir();

		File file = new File(localDir, filePath1);

		File file2 = new File(localDir, filePath2);
		if (file2.exists()) {
			Log.i(className, methodName + ": file: " + file2
					+ " exists! delete!");
			file2.delete();
		} else {
			Log.i(className, methodName + ": file: " + file2
					+ " not exists! mkdirs!");
			file2.getParentFile().mkdirs();
		}

		boolean success = file.renameTo(file2);

		if (success && file.getParentFile().listFiles().length == 0) {
			file.getParentFile().delete();
		}

		Log.i(className, methodName + ": Return: " + success);
		return success;

	}

	/** Move folder <username_cache>/folderpath to <username>/folderpath */
	public boolean moveFolder(String folderPath) {
		final String methodName = "moveFolderTo(" + folderPath + ")";
		Log.i(className, methodName + ": Entering...");

		List<FileType> fileTypes = this.getAllFileTypesbyFolder(folderPath);
		for (FileType fileType : fileTypes) {
			String key = fileType.getKey();
			int slashIdx = key.indexOf("/");
			String newKey = key.substring(slashIdx + 1);
			this.moveFileTo(key, newKey);
		}

		boolean success = true;
		if (!folderPath.equals(userName + "_cache/" + userName)) {

			File localDir = c.getFilesDir();
			File dir = new File(localDir, folderPath);

			success = dir.delete();
		}
		Log.i(className, methodName + ": Return: " + success);
		return success;
	}
}
